﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Lacrima.Framework.Infrastructure
{
    public static partial class CommonUtils
    {
        public static readonly DateTime defaultTime = DateTime.Parse("1900/01/01");

        /// <summary>
        /// 填充Model对象默认值
        /// </summary>
        /// <typeparam name="T">Model类型</typeparam>
        /// <param name="target">要填充默认值的对象</param>
        /// <returns>填充默认值之后的对象</returns>
        public static T Pad<T>(this T target) where T : class,new()
        {
            PropertyInfo[] properties = typeof(T).GetProperties();
            foreach (var property in properties)
            {
                if (property.SetMethod != null)
                {
                    Type type = property.PropertyType;
                    if (type.Equals(typeof(DateTime)))
                    {
                        property.SetValue(target, defaultTime);
                    }
                    else if (type.Equals(typeof(string)))
                    {
                        property.SetValue(target, string.Empty);
                    }
                    else if (typeof(Enum).IsAssignableFrom(type))
                    {
                        // Enum枚举类型不做处理
                        //property.SetValue(target, EnumUtils.GetIntValues(type).First());
                    }
                    else if (typeof(Exception).IsAssignableFrom(type))
                    {
                        // 异常类型不做处理
                    }
                    else
                    {
                        target = new T();
                        //property.SetValue(target, Activator.CreateInstance<T>());
                    }
                }
            }
            return target;
        }

        public static void Swap(ref int a, ref int b)
        {
            a ^= b;
            b ^= a;
            a ^= b;
        }

        /// <summary>
        /// 判断两个对象的各个属性值是否相等
        /// 该方法遵循值比较规则，例如DateTime比较时只比较ToString()的值是否相等
        /// （该方法为递归调用解决对象中的类成员变量判断值相等的问题，也可做适当修改使其变成非递归方式如下面的EqualsTo<T>方法就是调用了类自身实现的Equals方法）
        /// </summary>
        /// <typeparam name="T">比较的对象类型</typeparam>
        /// <param name="source">源对象</param>
        /// <param name="target">目标对象</param>
        /// <returns> 如果对象的各个属性值相等则返回true，否则返回false</returns>
        public static bool IsValueEquals<T>(this T source, T target) where T : class,new()
        {
            if (source == target)
                return true;

            // 注意这里两个对象为空值时也是相等的，否则在下面的递归调用中遇到字段为空时将返回false
            if (source == null || target == null)
                return (source == null && target == null);
            else
            {
                // 判断非集合类属性值是否相等,（注意：这里存在一个左侧的操作数为null的风险o.GetValue(source).IsValueEquals(o.GetValue(target)）
                return typeof(T).GetProperties().Where(n => !typeof(IEnumerable).IsAssignableFrom(n.PropertyType) || typeof(string).IsAssignableFrom(n.PropertyType))
                                                                .All(o => o.GetValue(source).Equals(o.GetValue(target)));
            }
        }

        /// <summary>
        /// 判断两个对象的各个属性值是否相等
        /// 该方法遵循系统自身判断规则，即null值比较时返回false，并且在成员变量为类对象时调用类自身的Equals方法
        /// (这里存在一个问题：如果类中的成员变量存在null值则直接返回false)
        /// </summary>
        /// <typeparam name="T">比较的对象类型</typeparam>
        /// <param name="source">源对象</param>
        /// <param name="target">目标对象</param>
        /// <param name="isStrictRule">是否使用严格的比较规则，true：严格规则，false：不严格规则</param>
        /// <returns>如果对象的各个属性值相等则返回true，否则返回false</returns>
        public static bool EqualsTo<T>(this T source, T target, bool isStrictRule = false, bool ignoreNull = true) where T : class,new()
        {
#if DEBUG
            string debugPropertyName = "debugProperty";
#endif

            if (source == target)
                return true;

            Type sourceType = source.GetType();
            // 注意这里两个对象为空值时也是相等的，否则在下面的递归调用中遇到字段为空时将返回false
            if (source == null || target == null)
            {
                if (isStrictRule)
                    return false;
                else
                    return (source == null && target == null);
            }
            else if (sourceType.IsPrimitive || sourceType.IsEnum || sourceType.IsValueType || typeof(string).IsAssignableFrom(sourceType))
            {
                // 这里的sourceType.IsPrimitive || sourceType.IsEnum可以省略，因为只用sourceType.IsValueType就够了
                return source.Equals(target);
            }
            else
            {
                bool flag = true;
                PropertyInfo[] properties = source.GetType().GetProperties();
                foreach (var property in properties)
                {

#if DEBUG  // 用于调试特殊字段时使用
                    if (property.Name.Equals(debugPropertyName))
                    {
                        Debug.WriteLine(debugPropertyName);
                    }
#endif
                    // 判断非集合类属性值是否相等，注意string类也继承了IEnumerable接口
                    if (!typeof(IEnumerable).IsAssignableFrom(property.PropertyType) || typeof(string).IsAssignableFrom(property.PropertyType))
                    {
                        if (property.GetValue(source) == null)
                        {
                            // 如果类中的成员变量存在null即返回false放开以下代码
                            if (isStrictRule) return false;
                            // 如果类中的成员变量不存在null
                            else if (ignoreNull) flag &= true;
                            else flag &= (property.GetValue(target) == null);
                        }
                        else
                        {
                            flag &= property.GetValue(source).EqualsTo(property.GetValue(target), isStrictRule, ignoreNull);
                        }

                        if (flag == false)
                        {
#if DEBUG
                            Debug.WriteLine(string.Format("The value of source property \"{0}\" not equals to the target!", property.Name));
                            Debug.WriteLine(string.Format("Compared property name is： {0}", property.Name));
                            Debug.WriteLine(string.Format("Compared property type is： {0}", property.PropertyType.Name));
                            Debug.WriteLine(string.Format("The source property value is： {0}", property.GetValue(source) == null ? "null" : property.GetValue(source)));
                            Debug.WriteLine(string.Format("The target property value is： {0}", property.GetValue(target) == null ? "null" : property.GetValue(target)));
#endif
                            return flag;
                        }
                    }
                }
                // 判断非集合类属性值是否相等
                return flag;
            }
        }

        /// <summary>
        /// 通过递归方式生成实体类对象，注意最大递归深度只有10
        /// </summary>
        /// <typeparam name="TEntity">实体类类型</typeparam>
        /// <returns>返回生成的实体类对象</returns>
        public static TResult RecursiveGenerate<TResult>(Type targetType = null, bool isRoot = true, int leafSize = 1, int level = 0)
            where TResult : class,new()
        {
            Type resultType = typeof(TResult).IsEquivalentTo(typeof(object)) ? targetType : typeof(TResult);

#if DEBUG
            // caution：这里出现异常是因为数组类型为基本类型数组，所以在递归的时候需要考虑到一个边界条件初始化处理的问题
            // 即通过MakeAixm创建基本类型时该如何处理。2016年11月8日20:00:20
            string debugPropertyName = "Text";
            Debug.WriteLine(string.Format("生成对象类型为：{0}", resultType.Name));
            Debug.WriteLine(string.Format("节点层级深度：{0}", level));
            Debug.WriteLine(string.Format("是否为根节点：{0}", isRoot));
#endif
            object entity = Activator.CreateInstance(resultType);                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        

            if (level > 10)
                return entity as TResult;

            PropertyInfo[] properties = resultType.GetProperties().Where(o => !o.PropertyType.IsAbstract).ToArray();

            foreach (PropertyInfo property in properties)
            {
#if DEBUG
                if (property.Name.Equals(debugPropertyName, StringComparison.OrdinalIgnoreCase))
                {
                    Debug.WriteLine("————————————————————————————————————");
                }
#endif
                Type propertyType = property.PropertyType;
                if (propertyType.IsEquivalentTo(typeof(DateTime)))
                {
                    property.SetValue(entity, DateTime.Now);
                }
                else if (propertyType.IsEquivalentTo(typeof(string)))
                {
                    property.SetValue(entity, GeneratorUtils.UID);
                }
                else if (propertyType.IsEnum)
                {
                    // 设置随机枚举值
                    property.SetValue(entity, GeneratorUtils.GetRandomEnumValue(propertyType));
                }
                else if (propertyType.IsValueType)
                {
                    property.SetValue(entity, Convert.ChangeType(GeneratorUtils.Rand.Next(), propertyType));
                }
                else if (propertyType.IsArray && !propertyType.IsAbstract)
                {
                    Type arrayElementType = propertyType.GetElementType();
                    var array = (IList)Array.CreateInstance(arrayElementType, leafSize);
#if DEBUG
                    Debug.WriteLine(string.Format("节点所在属性名称：{0}", property.Name));
                    Debug.WriteLine(string.Format("节点所在属性类型：{0}", propertyType.Name));
#endif
                    bool flag = arrayElementType.IsValueType || arrayElementType.IsEquivalentTo(typeof(string));
                    if (arrayElementType.IsEquivalentTo(typeof(string)))
                    {
                        // 对string类型数组进行初始化
                        for (int i = 0; i < leafSize; ++i)
                        {
                            array[i] = (GeneratorUtils.Rand.NextDouble() * 100).ToString();
                        }
                        property.SetValue(entity, array);
                    }
                    else if(!flag&& !arrayElementType.IsAbstract)
                    {
                        // 对其他类型进行初始化，
                        // #caution：注意这里忽略了其他值类型初始化的处理，例如bool，datetime类型和其他数值类型
                        for (int i = 0; i < leafSize; ++i)
                        {
                            array[i] = RecursiveGenerate<object>(arrayElementType, false, leafSize, level + 1);
                        }
                        property.SetValue(entity, array);
                    }
                }
                else
                {
#if DEBUG
                    Debug.WriteLine(string.Format("节点所在属性名称：{0}", property.Name));
                    Debug.WriteLine(string.Format("节点所在属性类型：{0}", propertyType.Name));
#endif

                    property.SetValue(entity, RecursiveGenerate<object>(propertyType, false, leafSize, level + 1));
                    // 此处对对象类的特殊字段进行处理
                    //if (property.PropertyType.IsEquivalentTo(typeof(CodeYesNoType)))
                    //{
                    //    var peopertyValue = property.GetValue(entity);
                    //    var defaultProperty = peopertyValue.GetDefaultProperty();
                    //    bool randValue = (int)Math.Ceiling(Math.Sin(random.Next())) == 1;
                    //    randValue.ToString().SetValueByProperty(peopertyValue, defaultProperty, null);
                    //}
                    //else if (typeof(CodeNorthReferenceType).IsEquivalentTo(property.PropertyType))
                    //{
                    //    var peopertyValue = property.GetValue(entity);
                    //    var defaultProperty = peopertyValue.GetDefaultProperty();
                    //    double randValue = random.NextDouble();
                    //    randValue.ToString().SetValueByProperty(peopertyValue, defaultProperty, null);
                    //}
                    //else if (typeof(DateYearType).IsEquivalentTo(property.PropertyType))
                    //{
                    //    var peopertyValue = property.GetValue(entity);
                    //    var defaultProperty = peopertyValue.GetDefaultProperty();
                    //    int randSpan = random.Next(32);
                    //    DateTime randValue = new DateTime((long)random.Next() << randSpan);
                    //    randValue.ToString().SetValueByProperty(peopertyValue, defaultProperty, null);
                    //}
                }
            }
            return entity as TResult;
        }

        /// <summary>
        /// 判断源对象是否在目标范围内
        /// </summary>
        /// <typeparam name="TElement">对象类型</typeparam>
        /// <param name="source">源对象</param>
        /// <param name="target">目标数组</param>
        /// <returns>true：存在，false：不存在</returns>
        public static bool In<TElement>(this TElement source, params TElement[] target)
        {
            if (source != null && target != null && target.Any())
            {
                return target.Any(i => source.Equals(i));
            }
            return false;
        }

        /// <summary>
        /// 逐个字段复制并生成新对象
        /// </summary>
        /// <typeparam name="TElement">The type of the t element.</typeparam>
        /// <param name="source">The source.</param>
        /// <returns>``0.</returns>
        public static TElement Clone<TElement>(this TElement source)
            where TElement : class,new()
        {
            TElement result = new TElement();
            if (source != null)
            {
                PropertyInfo[] properties = typeof(TElement).GetProperties();
                foreach (var property in properties)
                {
                    property.SetValue(result, property.GetValue(source));
                }
            }
            return result;
        }

        /// <summary>
        /// 通过key值获取字典中的value
        /// </summary>
        /// <param name="dictionary">数据字典</param>
        /// <param name="key">key值</param>
        /// <returns>Value值</returns>
        public static string GetDictionaryValue(this IDictionary<string, string> dictionary, string key)
        {
            if (!string.IsNullOrEmpty(key) && dictionary != null && dictionary.Any() && dictionary.ContainsKey(key))
            {
                return dictionary[key];
            }
            return string.Empty;
        }
    }
}
